package weiyao.xinxidasai.Utils.OSS;

import com.aliyun.oss.OSS;
import com.aliyun.oss.model.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

@Component
public class OSSDownloader {
    private static final Logger logger = LoggerFactory.getLogger(OSSDownloader.class);

    private final OSS ossClient;
    private final String bucketName;
    private final int chunkSize; // 用于判断是否分片的阈值
    private final String downloadTempDir;
    private ExecutorService downloadExecutor; // 下载线程池

    // 下载分片大小（硬编码为512KB）
    private static final int DOWNLOAD_CHUNK_SIZE = 512 * 1024; // 512KB
    // 并发线程数（硬编码为20）
    private static final int DOWNLOAD_CONCURRENCY = 10;

    public OSSDownloader(OSS ossClient,
                         @Value("${oss.bucket-name}") String bucketName,
                         @Value("${oss.chunk-size-mb:10}") int chunkSizeMb,
                         @Value("${download.temp.dir:/tmp/downloads}") String downloadTempDir) {
        this.ossClient = ossClient;
        this.bucketName = bucketName;
        this.chunkSize = chunkSizeMb * 1024 * 1024; // 转换为字节
        this.downloadTempDir = downloadTempDir;
    }

    @PostConstruct
    public void init() {
        createTempDirectory();
        // 初始化下载线程池
        downloadExecutor = Executors.newFixedThreadPool(DOWNLOAD_CONCURRENCY);
    }

    private void createTempDirectory() {
        File dir = new File(downloadTempDir);
        if (!dir.exists()) {
            if (dir.mkdirs()) {
                logger.info("创建下载临时目录: {}", downloadTempDir);
            } else {
                logger.error("无法创建下载临时目录: {}", downloadTempDir);
            }
        }
    }

    /**
     * 下载OSS文件到本地
     * @param objectKey OSS存储路径
     * @param targetFile 目标文件
     * @return 下载结果
     */
    public boolean downloadFile(String objectKey, File targetFile) {
        try {
            ObjectMetadata metadata = getObjectMetadata(objectKey);
            long fileSize = metadata.getContentLength();

            if (fileSize <= chunkSize) {
                logger.info("使用普通下载: {}", objectKey);
                return downloadSmallFile(objectKey, targetFile);
            } else {
                logger.info("使用分片下载: {} (大小: {} bytes)", objectKey, fileSize);
                return downloadLargeFile(objectKey, targetFile, fileSize);
            }
        } catch (Exception e) {
            logger.error("文件下载失败: {}", objectKey, e);
            return false;
        }
    }

    /**
     * 获取文件元数据
     */
    private ObjectMetadata getObjectMetadata(String objectKey) {
        return ossClient.getObjectMetadata(bucketName, objectKey);
    }

    /**
     * 小文件直接下载
     */
    private boolean downloadSmallFile(String objectKey, File targetFile) {
        try (InputStream inputStream = ossClient.getObject(bucketName, objectKey).getObjectContent()) {
            // 确保目标目录存在
            if (!targetFile.getParentFile().exists()) {
                targetFile.getParentFile().mkdirs();
            }

            try (RandomAccessFile raf = new RandomAccessFile(targetFile, "rw")) {
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    raf.write(buffer, 0, bytesRead);
                }
            }
            return true;
        } catch (IOException e) {
            logger.error("小文件下载失败: {}", objectKey, e);
            return false;
        }
    }

    /**
     * 大文件分片下载（断点续传） - 优化版
     */
    private boolean downloadLargeFile(String objectKey, File targetFile, long fileSize) {
        // 创建临时目录
        File tempDir = new File(downloadTempDir, "chunks_" + System.currentTimeMillis());
        if (!tempDir.exists() && !tempDir.mkdirs()) {
            logger.error("无法创建分片临时目录: {}", tempDir.getAbsolutePath());
            return false;
        }

        try {
            // 计算分片数量（使用512KB分片）
            int chunkCount = (int) Math.ceil((double) fileSize / DOWNLOAD_CHUNK_SIZE);
            logger.info("开始分片下载，共{}个分片，分片大小: {}KB", chunkCount, DOWNLOAD_CHUNK_SIZE / 1024);

            // 使用原子计数器跟踪进度
            AtomicInteger completedChunks = new AtomicInteger(0);

            // 提交所有分片下载任务
            for (int i = 0; i < chunkCount; i++) {
                final int chunkIndex = i;
                downloadExecutor.submit(() -> {
                    long start = (long) chunkIndex * DOWNLOAD_CHUNK_SIZE;
                    long end = Math.min(start + DOWNLOAD_CHUNK_SIZE - 1, fileSize - 1);

                    File chunkFile = new File(tempDir, "chunk_" + chunkIndex);
                    if (!downloadChunk(objectKey, chunkFile, start, end)) {
                        logger.error("分片下载失败: 分片 {}", chunkIndex);
                    } else {
                        int progress = completedChunks.incrementAndGet();
                        if (progress % 10 == 0 || progress == chunkCount) {
                            logger.info("下载进度: {}/{} ({}%)", progress, chunkCount,
                                    (int) (progress * 100.0 / chunkCount));
                        }
                    }
                });
            }

            // 等待所有任务完成
            downloadExecutor.shutdown();
            if (!downloadExecutor.awaitTermination(1, TimeUnit.HOURS)) {
                logger.error("下载任务超时未完成");
                return false;
            }

            // 检查是否所有分片都下载成功
            File[] chunkFiles = tempDir.listFiles();
            if (chunkFiles == null || chunkFiles.length != chunkCount) {
                logger.error("分片数量不匹配! 预期: {}, 实际: {}", chunkCount,
                        chunkFiles != null ? chunkFiles.length : 0);
                return false;
            }

            // 合并分片
            return mergeChunks(tempDir, targetFile, fileSize);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            logger.error("下载任务被中断", e);
            return false;
        } finally {
            // 清理临时文件
            deleteDirectory(tempDir);
            // 重置线程池
            downloadExecutor = Executors.newFixedThreadPool(DOWNLOAD_CONCURRENCY);
        }
    }

    /**
     * 下载单个分片
     */
    public boolean downloadChunk(String objectKey, File chunkFile, long start, long end) {
        GetObjectRequest request = new GetObjectRequest(bucketName, objectKey)
                .withRange(start, end);

        try (InputStream inputStream = ossClient.getObject(request).getObjectContent();
             RandomAccessFile raf = new RandomAccessFile(chunkFile, "rw")) {

            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                raf.write(buffer, 0, bytesRead);
            }
            return true;
        } catch (IOException e) {
            logger.error("分片下载失败: {}-{}", start, end, e);
            return false;
        }
    }

    /**
     * 下载分片数据流（用于流式处理）
     */
    public InputStream downloadChunkStream(String objectKey, long start, long end) {
        GetObjectRequest request = new GetObjectRequest(bucketName, objectKey)
                .withRange(start, end);
        return ossClient.getObject(request).getObjectContent();
    }

    /**
     * 合并分片文件
     */
    private boolean mergeChunks(File tempDir, File targetFile, long expectedSize) {
        // 确保目标目录存在
        if (!targetFile.getParentFile().exists() && !targetFile.getParentFile().mkdirs()) {
            logger.error("无法创建目标目录: {}", targetFile.getParent());
            return false;
        }

        try (RandomAccessFile targetRaf = new RandomAccessFile(targetFile, "rw")) {
            File[] chunks = tempDir.listFiles();
            if (chunks == null || chunks.length == 0) {
                logger.error("没有找到分片文件");
                return false;
            }

            // 按文件名排序确保顺序正确
            Arrays.sort(chunks, (f1, f2) -> {
                try {
                    int idx1 = Integer.parseInt(f1.getName().split("_")[1]);
                    int idx2 = Integer.parseInt(f2.getName().split("_")[1]);
                    return Integer.compare(idx1, idx2);
                } catch (Exception e) {
                    logger.error("分片文件名解析错误", e);
                    return 0;
                }
            });

            logger.info("开始合并{}个分片...", chunks.length);
            long totalWritten = 0;

            // 合并文件
            for (File chunk : chunks) {
                try (RandomAccessFile chunkRaf = new RandomAccessFile(chunk, "r")) {
                    byte[] buffer = new byte[8192];
                    int bytesRead;
                    while ((bytesRead = chunkRaf.read(buffer)) != -1) {
                        targetRaf.write(buffer, 0, bytesRead);
                        totalWritten += bytesRead;
                    }
                }
                // 记录合并进度
                logger.debug("已合并分片: {}, 累计大小: {}MB", chunk.getName(),
                        totalWritten / (1024 * 1024));
            }

            // 验证文件大小
            if (targetFile.length() != expectedSize) {
                logger.error("文件大小不匹配! 预期: {} bytes, 实际: {} bytes",
                        expectedSize, targetFile.length());
                return false;
            }

            logger.info("文件合并成功，大小: {}MB", targetFile.length() / (1024 * 1024));
            return true;
        } catch (IOException e) {
            logger.error("分片合并失败", e);
            return false;
        }
    }

    /**
     * 递归删除目录
     */
    private void deleteDirectory(File dir) {
        if (dir == null || !dir.exists()) return;

        File[] files = dir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    deleteDirectory(file);
                } else {
                    if (!file.delete()) {
                        logger.warn("无法删除临时文件: {}", file.getAbsolutePath());
                    }
                }
            }
        }

        if (!dir.delete()) {
            logger.warn("无法删除临时目录: {}", dir.getAbsolutePath());
        }
    }


}